<html>
    <head>
        <title>使用引用来应用值</title>
    </head>
    <script>
        // 使用引用来引用值
                /*
                    当你想要一个组件"记住"一些信息，但你又不希望信息会"触发新渲染"的时候，你可以使用"引用"
                */

                // 一、向你的组件添加引用
                        // 你可以通过 React 导入 useRef 钩子来为你的组件添加一个引用：
                            import {useRef} from 'react';

                        // 在你的组件内部，调用 useRef 钩子并将你要引用的初始值作为唯一参数传递。  
                            // 对值0的引用
                            const ref = useRef(0);

                        /*
                            useRef 钩子函数，返回一个这样的对象：

                                {
                                    current:0 // 你传递给引用的初始值
                                }
                        */ 

                        /*
                            你可以通过 ref.current 属性访问该引用的当前值。
                            这个值是"有意可变的"，这意味着你可以"读取"和"写入"它。
                            它就像是React无法跟踪的组件的秘密口袋。（这就是使它成为来自React的单项数据流的"应急方案"的原因）
                        */

                            // eg: 一个按钮，将在每次点击时递增　　ref.current:
                                import { useRef } from 'react';  // 导入钩子

                                export default function Counter() {
                                    let ref = useRef(0);  // 创建组件的引用

                                    function handleClick() {
                                        ref.current = ref.current + 1;  // 修改引用值
                                        alert('You clicked ' + ref.current + ' times!');
                                    }

                                    return (
                                        <button onClick={handleClick}>
                                            Click me!
                                        </button>
                                    );
                                }

                        /*
                            引用指向一个数字，但是，就像"状态(state)".你可以指向任何东西： 一个字符串、一个对象、甚至一个函数。

                            与状态不同，引用是一个"具有current属性的普通的JavaScript对象"，你可以"读取"和"修改"它。

                            请注意，组件不会随着每次增量而而重新渲染。与状态一样，引用在重新渲染之前由React保存。但是，设置状态会触发重新渲染组价。但，修改引用不会触发渲染。
                        */

                               // 示例： 构建秒表
                                        import { useState } from 'react';

                                        export default function Stopwatch() {
                                            const [startTime, setStartTime] = useState(null);
                                            const [now, setNow] = useState(null);

                                            function handleStart() {
                                                // Start counting.
                                                setStartTime(Date.now());
                                                setNow(Date.now());

                                                setInterval(() => {
                                                //每10毫秒更新一次now状态，这将触发重新渲染。
                                                setNow(Date.now());
                                                }, 10);
                                            }

                                            let secondsPassed = 0;
                                            if (startTime != null && now != null) {
                                                secondsPassed = (now - startTime) / 1000;
                                            }

                                            return (
                                                <>
                                                <h1>Time passed: {secondsPassed.toFixed(3)}</h1>
                                                    <button onClick={handleStart}>
                                                        Start
                                                    </button>
                                                </>
                                            );
                                        }
                                        /*
                                            当按下"停止"按钮时，需要取消已有的时间间隔，使其停止更新now状态变量。
                                            你可以通过调用 clearInterval 来执行此操作，但是你需要为其提供先前用户按下时返回的时间间隔ID. 
                                            你需要将间隔ID保存在某处。由于间隔　ID不用于渲染，你可以将其保存在引用中。
                                        */
                                            // 代码修改后：
                                                import { useState, useRef } from 'react';

                                                export default function Stopwatch() {
                                                    const [startTime, setStartTime] = useState(null);
                                                    const [now, setNow] = useState(null);
                                                    const intervalRef = useRef(null);

                                                    function handleStart() {
                                                        setStartTime(Date.now());
                                                        setNow(Date.now());

                                                        clearInterval(intervalRef.current); // 先清除间隔
                                                        intervalRef.current = setInterval(() => { // 间隔对象保存到引用
                                                        setNow(Date.now());
                                                        }, 10);
                                                    }

                                                    function handleStop() {
                                                        clearInterval(intervalRef.current);  // 清楚间隔 
                                                    }

                                                    let secondsPassed = 0;
                                                    if (startTime != null && now != null) {
                                                        secondsPassed = (now - startTime) / 1000;
                                                    }

                                                    return (
                                                        <>
                                                            <h1>Time passed: {secondsPassed.toFixed(3)}</h1>
                                                            <button onClick={handleStart}>
                                                                Start
                                                            </button>
                                                            <button onClick={handleStop}>
                                                                Stop
                                                            </button>
                                                        </>
                                                    );
                                                }

            // 二、引用与状态的区别：
                    /*
                        也许你认为引用看起来比状态少了严格----例如，你可以修改它们，而不必总是使用"状态设置函数"。
                        但在大多数情况下，你会希望使用状态。 引用是不不会经常使用的"应急方案"。

                                以下是状态和引用的比较：

                                引用：                                                      状态：
                                useRef(initialValue) 返回{current:initialValue}             useState(initialValue) 返回"状态变量的当前值" 和 "状态设置函数声明"

                                更改时不会触发重新渲染                                        更改时会触发重新渲染

                                可变--你可以在渲染过程中修改和更新current值                    "不变的"---你必须使用状态设置功能来修改状态变量一对重进渲染进行排队

                                你不应该在渲染期间读取(或写入) current值                      你可以随时读取状态。但是每个渲染器(每次渲染)都有自己的"状态快照"不会改变



                    */
                                                


            // 三、何时使用引用？
                    /*
                         通常，当你的组件需要"走出"React并与外部API（通常是不会影响组件外观的浏览器API）通信时，你将使用ref.

                         以下是其中一些罕见的情况：
                                1、存储"超时ID"
                                2、存储和操作"DOM"元素，我们将在下一节中介绍
                                3、存储计算JSX不需要的其他对象


                         总之，如果你的组件需要存储一些信息，但不影响渲染逻辑，请选择引用。
                    */

            // 四、引用的最佳实践：
                    /*
                        遵循这些原则将使你的组件更具可预测性：
                               1、将引用视为应急方案。
                                            当你使用外部系统或浏览器 API 时，引用很有用。
                                            如果你的大部分应用逻辑和数据流都依赖于引用，你可能需要重新考虑你的方法。       
                               2、不要在渲染过程中读取或写入 ref.current。
                                            如果在渲染过程中需要一些信息，请改用"状态。"
                                            由于React不知道 ref.current 何时更改，即使在渲染时读取它也会让组件的行为难以预测。
                                            （唯一的例外是像 if (!ref.current) ref.current = new Thing() 这样的代码，它在第一次渲染期间只设置一次引用。）

                        React 状态的限制不适用于引用。 例如，状态就像 "每个渲染的快照" 和 "不同步更新" 。但是当你该百年引用的当前值时，它会立即改变。

                        这是因为，引用本身就是一个常规的JavaScript 对象，因此它的行为就像普通的对象一样。
                        当你使用引用时，也不必担心"需要避免冲突"。只要你正在改变的对象不同意渲染，React就不会关系你对引用或其内容做了什么。
                    */


            // 五、引用与DOM
                    /*
                        你可以将引用指向任何值。但是，引用最常见的例子就是访问DOM元素。
                        例如，如果你想以编程方式聚焦输入，这会很方便。
                        当你将引用传递给JSX中的ref属性时，如 <div ref={myRef}> ，React会将相应的Dom元素放入 myRef.current。一旦元素从DOM中删除，React就会将 myRef.current设置为null
                    */


    </script>
</html>